home *** CD-ROM | disk | FTP | other *** search
- /*
- * CBLibrary - FedCompMT
- * Copyright (C) 2003 Chris Bazley
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * This library is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-
- /*
- Fednet-style file compression/decompression (stand alone)
- Original version by David O'Shea
- */
-
- /* ANSI library files */
- #include <stdlib.h>
- #include <stdio.h>
- #include <string.h>
- #include <stdbool.h>
- #include <limits.h>
-
- /* RISC OS library files */
- #include "kernel.h"
- #include "flex.h"
-
- /* Other headers */
- #include "msgtrans.h"
- #include "hourglass.h"
- #include "Macros.h"
- #include "FopenCount.h"
- #include "FedCompMT.h"
-
- typedef struct _decomp_state {
- FILE *f;
- long int read_pos;
- unsigned int len;
- unsigned int dptr;
- unsigned int r6;
- } decomp_state;
-
- typedef struct _comp_state {
- FILE *f;
- unsigned int len;
- unsigned int save_state;
- int cptr;
- int posn;
- } comp_state;
-
- extern _kernel_oserror shared_err_block;
-
- /* ----------------------------------------------------------------------- */
- /* Function prototypes */
-
- static bool loadbits(decomp_state *state, unsigned int en, unsigned int *ret_r9);
- static unsigned int findseq(comp_state *state, char **buffer_anchor);
- static bool savebits(comp_state *state, unsigned int nbits, unsigned int bits);
-
- /* ----------------------------------------------------------------------- */
- /* Public functions */
-
- unsigned int get_decomp_perc(FILE ***handle)
- {
- decomp_state *state = (decomp_state *)*handle;
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report get_decomp_perc state->dptr: %d len: %d", state->dptr, state->len);
- _kernel_oscli(string);
- }
- #endif
- if(state->len == 0)
- return 100; /* guard against divide-by-zero */
-
- if(state->dptr < (ULONG_MAX/100))
- return (unsigned int)(state->dptr*100) / state->len;
- else
- return (unsigned int)(((float)state->dptr*100) / state->len);
- }
-
- /* ----------------------------------------------------------------------- */
-
- unsigned int get_comp_perc(FILE ***handle)
- {
- comp_state *state = (comp_state *)*handle;
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report get_comp_perc state->cptr: %d state->len: %d", state->cptr, state->len);
- _kernel_oscli(string);
- }
- #endif
-
- if(state->len == 0)
- return 100; /* guard against divide-by-zero */
- if(state->cptr < (ULONG_MAX/100))
- return (unsigned int)(state->cptr*100) / state->len;
- else
- return (unsigned int)(((float)state->cptr*100) / state->len);
- }
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *load_compressedM(const char *filepath, flex_ptr buffer_anchor, const volatile bool *timeup, FILE ***handle)
- {
- decomp_state *state;
-
- _kernel_last_oserror(); /* reset SCL's error recording */
-
- if(*handle == NULL) {
- /* Starting afresh */
- #ifndef NDEBUG
- _kernel_oscli("report starting decompression process");
- #endif
-
- state = (decomp_state *)malloc(sizeof(decomp_state));
- if(state == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* fail */
- }
- state->dptr = 0;
- state->r6 = 8;
- state->f = NULL;
- state->read_pos = 0;
-
- } else {
- /* Continue from where we left off */
- state = (decomp_state *)*handle;
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report continuing decompression from dptr %d", state->dptr);
- _kernel_oscli(string);
- #endif
- }
-
- if(state->f == NULL) {
- /* (Re)open file */
- state->f = fopen_inc(filepath, "rb"); /* open for reading */
- if(state->f == NULL) {
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "OpenInFail", filepath);
- return &shared_err_block; /* fail */
- }
- fseek(state->f, state->read_pos, SEEK_SET); /* start reading data where we left off */
- }
-
- if(*handle == NULL) {
- /* Get size of decompressed data */
- #ifndef NDEBUG
- _kernel_oscli("report Reading size of decompressed data");
- #endif
- if(fread(&state->len, sizeof(unsigned int), 1, state->f) != 1) {
- fclose_dec(state->f);
- free(state);
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "ReadFail", filepath);
- return &shared_err_block; /* fail */
- }
-
- /* Allocate buffer for data */
- #ifndef NDEBUG
- _kernel_oscli("report Allocating buffer for data");
- #endif
- if(!flex_alloc(buffer_anchor, state->len)) {
- fclose_dec(state->f);
- free(state);
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* fail */
- }
- }
-
- #ifndef NDEBUG
- if(*timeup)
- _kernel_oscli("report timeup before start");
- #endif
-
- do {
- unsigned int r9;
-
- //#ifndef NDEBUG
- // char string[255];
- // sprintf(string, "report fpos %ld", ftell(state->f));
- // _kernel_oscli(string);
- //#endif
- if(!loadbits(state, 1, &r9))
- break; /* read error or EOF */
- if (r9 != 1) {
- unsigned int r9;
- if(!loadbits(state, 8, &r9))
- break; /* read error or EOF */
- if(state->dptr < state->len)
- ((char *)*buffer_anchor)[state->dptr] = (char)r9;
- state->dptr++;
- } else {
- unsigned int chunk_len, read_pos;
- if(!loadbits(state, 9, &read_pos))
- break; /* read error or EOF */
- if (read_pos >= 256) {
- if(!loadbits(state, 8, &chunk_len))
- break; /* read error or EOF */
- } else {
- if(!loadbits(state, 9, &chunk_len))
- break; /* read error or EOF */
- }
- {
- int read_offset = (state->dptr - 512) + read_pos;
- unsigned int chunk_offset = 0;
- char *ba = (char *)*buffer_anchor; /* careful! flex must not budge */
- do {
- char output_byte;
- if(read_offset < 0)
- output_byte = 0;
- else
- output_byte = ba[read_offset];
- if((state->dptr + chunk_offset) < state->len)
- ba[state->dptr + chunk_offset] = (char)output_byte;
- else {
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
- #ifndef NDEBUG
- _kernel_oscli("report Error in compressed bitstream");
- #endif
- WRITE_ERR(shared_err_block, "BitStream");
- return &shared_err_block; /* fail */
- }
-
- read_offset++;
- chunk_offset++;
- } while (chunk_offset < chunk_len);
- }
- state->dptr += chunk_len;
- }
- } while(*timeup == false);
-
- if(ferror(state->f)) {
- /* File error on fgetc() */
- #ifndef NDEBUG
- _kernel_oscli("report Aborting - file error on fgetc()");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "ReadFail", filepath);
- return &shared_err_block; /* fail */
- }
-
- if(feof(state->f)) {
- /* Finished (got to end of input) */
- #ifndef NDEBUG
- _kernel_oscli("report Decompression complete (EOF)");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
- } else {
- /* Stopped before EOF */
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report Pausing decompression at dptr %d", state->dptr);
- _kernel_oscli(string);
- #endif
- if(fopen_num() >= FOPEN_MAX) {
- /* if we have no spare file handles then close file */
- state->read_pos = ftell(state->f);
- fclose_dec(state->f);
- state->f = NULL;
- }
- *handle = (FILE **)state; /* write back pointer to state */
- }
-
- return NULL; /* no error */
- }
-
- /* ----------------------------------------------------------------------- */
-
- _kernel_oserror *save_compressedM(const char *filepath, int filetype, flex_ptr buffer_anchor, const volatile bool *timeup, FILE ***handle)
- {
- comp_state *state;
-
- _kernel_last_oserror(); /* reset SCL's error recording */
-
- if(*handle == NULL) {
- /* Starting afresh */
- #ifndef NDEBUG
- _kernel_oscli("report starting compression process");
- #endif
-
- state = (comp_state *)malloc(sizeof(comp_state));
- if(state == NULL) {
- WRITE_GERR(shared_err_block, "NoMem");
- return &shared_err_block; /* fail */
- }
- state->len = flex_size(buffer_anchor);
- state->cptr = 0;
- state->save_state = 0;
- state->f = fopen_inc(filepath, "wb"); /* open for writing */
-
- } else {
- /* Continue from where we left off */
- state = (comp_state *)*handle;
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report continuing compression from cptr %d", state->cptr);
- _kernel_oscli(string);
- #endif
- if(state->f == NULL)
- state->f = fopen_inc(filepath, "ab"); /* re-open for appending */
- }
-
- if(state->f == NULL) {
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "OpenOutFail", filepath);
- return &shared_err_block; /* fail */
- }
-
- if(*handle == NULL) {
- /* Write size of uncompressed data */
- if(fwrite(&state->len, sizeof(unsigned int), 1, state->f) != 1)
- goto err;
- }
-
- #ifndef NDEBUG
- if(*timeup)
- _kernel_oscli("report timeup before start");
- #endif
-
- while(state->cptr < state->len) {
- char **ba = (char **)buffer_anchor;
- unsigned int found;
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report fpos %ld", ftell(state->f));
- _kernel_oscli(string);
- }
- #endif
- found = findseq(state, ba);
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report found:%d cptr:%d posn:%d", found, state->cptr, state->posn);
- _kernel_oscli(string);
- }
- #endif
- if(found < 2) {
- if(!savebits(state, 9, (*ba)[state->cptr] << 1))
- goto err; /* write error */
- state->cptr++;
- } else {
- if(!savebits(state, 10, (state->posn - state->cptr << 1) | 1))
- goto err; /* write error */
- if((state->posn + 256) < state->cptr) {
- if(!savebits(state, 9, found))
- goto err; /* write error */
- } else {
- if(!savebits(state, 8, found))
- goto err; /* write error */
- }
- state->cptr += found;
- }
- if(*timeup == true)
- break; /* out of time */
- } /* endwhile */
-
- if(state->cptr >= state->len) {
- /* Finished (got to end of input) */
- if(!savebits(state, 0, 0))
- goto err; /* write error */
- #ifndef NDEBUG
- _kernel_oscli("report Compression complete");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
-
- /* Set file type (beware of doing this before closing file!) */
- {
- _kernel_osfile_block inout;
- inout.load = filetype;
- if(_kernel_osfile(18, filepath, &inout) == _kernel_ERROR)
- return _kernel_last_oserror(); /* failure */
- }
-
- } else {
- /* Assume stopped cos of time out */
- #ifndef NDEBUG
- char string[255];
- sprintf(string, "report Pausing compression at cptr %d", state->cptr);
- _kernel_oscli(string);
- #endif
- if(fopen_num() >= FOPEN_MAX) {
- /* if we have no spare file handles then close file */
- fclose_dec(state->f);
- state->f = NULL;
- }
- *handle = (FILE **)state; /* write back pointer to state */
- }
- return NULL; /* success */
-
- err:
- /* File error on fputc() or fwrite() */
- #ifndef NDEBUG
- _kernel_oscli("report Aborting - file error on fputc() or fwrite()");
- #endif
- fclose_dec(state->f);
- free(state);
- *handle = NULL; /* write back NULL pointer */
- THROW(_kernel_last_oserror()) /* any OS error? */
- WRITE_ERR_SUB1(shared_err_block, "WriteFail", filepath);
- return &shared_err_block; /* fail */
- }
-
- /* ----------------------------------------------------------------------- */
- /* Private functions */
-
- static bool loadbits(decomp_state *state, unsigned int en, unsigned int *ret_r9)
- {
- /* Returns false to indicate read error or end of file */
- unsigned int r6, r9, shifty;
-
- r9 = 0;
- r6 = state->r6;
- for (shifty = 0; shifty < en; shifty++) {
- if ((r6 & 0xff) == 8) {
- int r0 = fgetc(state->f);
- if(r0 == EOF) {
- state->r6 = r6;
- return false; /* read error or EOF */
- } else
- r6 = r0<<8;
- }
- {
- int r14 = (r6>>8);
- r6 &= 0xff;
- r6 += r14<<8;
- {
- int r12 = r14 & (1<<r6);
- if(r12 > 0)
- r12 = 1;
- r9 += (r12<<shifty);
- }
- }
- r6++;
- }
- state->r6 = r6;
- *ret_r9 = r9;
- return true; /* success */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static bool savebits(comp_state *state, unsigned int nbits, unsigned int bits)
- {
- /* Returns false to indicate write error */
- unsigned int posn, output;
-
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report savebits save_state:%X nbits:%d bits:%X", state->save_state, nbits, bits);
- _kernel_oscli(string);
- }
- #endif
- posn = state->save_state & 0xff;
- output = state->save_state >> 8;
-
- if(nbits == 0) {
- if(posn > 0) {
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report fputc %lX:%X", ftell(state->f), output);
- _kernel_oscli(string);
- }
- #endif
- if(fputc(output, state->f) == EOF)
- return false; /* write error */
- }
- return true; /* success */
- }
- {
- unsigned int bit;
- for(bit = 0; bit < nbits; bit++) {
- output = output | ((bits & 1) << posn);
- bits >>= 1;
- posn++;
- if(posn == 8) {
- #ifndef NDEBUG
- {
- char string[255];
- sprintf(string, "report fputc %lX:%X", ftell(state->f), output);
- _kernel_oscli(string);
- }
- #endif
- if(fputc(output, state->f) == EOF)
- return false; /* write error */
- posn = output = 0;
- }
- }
- }
- state->save_state = (output << 8) | posn;
- return true; /* success */
- }
-
- /* ----------------------------------------------------------------------- */
-
- static unsigned int findseq(comp_state *state, char **buffer_anchor)
- {
- unsigned int mcount = 0;
- char *ba = *buffer_anchor; /* careful! flexblock must stay put */
-
- if (ba[state->cptr] == 0 && ba[state->cptr+1] == 0) {
- unsigned int seqptr = state->cptr;
- state->posn = state->cptr - 512;
- for(mcount = 0; mcount < 511; mcount++) {
- if(ba[seqptr] != 0
- || seqptr >= state->len
- || (state->posn + (int)mcount >= 0 && ba[state->posn + mcount] != 0))
- break;
- seqptr++;
- }
- }
-
- {
- int buff;
- unsigned int max;
-
- if(state->cptr < 511)
- buff = 0;
- else
- buff = state->cptr - 511;
-
- if(state->cptr > state->len)
- max = state->len;
- else
- max = state->cptr;
-
- while(buff < max) {
- int bufcompfptr;
- unsigned int seqptr = state->cptr;
-
- for(bufcompfptr = buff; bufcompfptr < max; bufcompfptr++) {
- if(ba[bufcompfptr] == ba[seqptr]
- || seqptr >= state->len)
- break;
- }
-
- if(bufcompfptr >= max)
- return mcount;
-
- {
- unsigned int count, maxcount;
- int start = bufcompfptr;
-
- if((bufcompfptr + 256) < state->cptr)
- maxcount = 511;
- else
- maxcount = 255;
-
- {
- unsigned int seqptr = state->cptr;
- for(count = 0; count < maxcount; count++) {
- if(ba[bufcompfptr] != ba[seqptr]
- || bufcompfptr >= max
- || seqptr >= state->len)
- break;
- bufcompfptr++;
- seqptr++;
- }
- }
-
- if(count > mcount) {
- mcount = count;
- state->posn = start;
- }
- buff = start + 1;
- }
- }
- }
- return mcount;
- }
-